#include "ET++.ph"
#ifdef __GNUG__
#pragma implementation
#endif

#include "Port.h"

#include "Class.h"
#include "String.h"
#include "Error.h"
#include "Picture.h"
#include "WindowSystem.h"
#include "DevBitmap.h"
#include "Math.h"

const int MaxTextBatchCnt= 400;

Port *Port::port, *Port::tbport;
Ink *Port::tbink= 0;
Point Port::tbpos, Port::tblastpos;
Rectangle Port::tbbbox;

static inline void ScaleRect(Port *p, Rectangle &r)
{
	if (p->scale & 1) {
		r.origin.x= (int) ((float)r.origin.x * p->scalex + 0.5);
		r.origin.y= (int) ((float)r.origin.y * p->scaley + 0.5);
		r.extent.x= (int) ((float)r.extent.x * p->scalex + 0.5);
		r.extent.y= (int) ((float)r.extent.y * p->scaley + 0.5);
	}
}

static inline void ScalePoint(Port *p, Point &pp)
{
	if (p->scale & 1) {
		pp.x= (int) ((float)pp.x * p->scalex + 0.5);
		pp.y= (int) ((float)pp.y * p->scaley + 0.5);
	}
}

static inline void ScaleInt(Port *p, int &i)
{
	if (p->scale & 1)
		i= (int) ((float)i * p->scalex + 0.5);
}

GrState::GrState(Port *p)
{
	if (p == 0)
		port= Port::port;
	else
		port= p;
	clip= port->cliprect;
	origin= port->origin;
	scalex= port->scalex;
	scaley= port->scaley;
}

GrState::~GrState()
{
	port->FlushMyText();
	port->sfont= 0;
	port->DevScale(scalex, scaley);
	port->origin= origin;
	port->Clip(clip);
}

//---- Port --------------------------------------------------------------------

NewMetaImpl(Port,Object, (T(depth), T(hascolor)));

Port::Port()
{
	PortInit();
	hascolor= FALSE;
	depth= gDepth;
}

Port::~Port()
{
	if (port == this)
		port= 0;
	if (tbport == this)
		tbport= 0;
}

void Port::PortInit()
{
	scalex= scaley= 1.0;
	scale= FALSE;
	cliprect= Rectangle(0, 0, 30000, 30000);
	origin= gPoint0;
	SetNormal();
	SetPenNormal();
	SetTextNormal();
}

//---- abstract cursor methods -------------------------------------------------

GrCursor Port::SetCursor(GrCursor c)
{
	return c;
}

GrCursor Port::SetWaitCursor(unsigned int, GrCursor c)
{
	if (this)
		return SetCursor(c);
	return c;
}

GrCursor Port::GetCursor()
{
	return eCrsNone;
}

//---- graphics context --------------------------------------------------------

void Port::SetPenNormal()
{
	pensize= 1;
	penink= gInkBlack;
	pencap= eDefaultCap;
}

void Port::SetTextNormal()
{
	textink= gInkBlack;
	textfont= gSysFont;
}

//---- color halftoning (ordered dither) ---------------------------------------

extern Ink *Grey2Halftone(float f);

void Port::DevSetGrey(float f)
{
	InstallInk(Grey2Halftone(f));
}

void Port::SetColor(RGBColor *cp)
{
	if (HasColor())
		DevSetColor(cp);
	else
		DevSetGrey(cp->AsGreyLevel() / 255.0);
}

//---- clipping ----------------------------------------------------------------

void Port::InitClip()
{
	FlushMyText();
	sfont= 0;
	DevScale(1.0, 1.0);
	cliprect= Rectangle(0, 0, 30000, 30000);
	DevClip(cliprect);
	origin= gPoint0;
}

void Port::Clip(const Rectangle &r)
{
	if (cliprect != r) {
		FlushMyText();
		cliprect= r;
		DevClip(r);
		// scale|= 2;
	}
}

void Port::ClipFurther(const Rectangle &rr)
{
	Rectangle r(rr);
	ScaleRect(this, r);
	r.origin+= origin;
	r.Clip(cliprect);
	Clip(r);
}

void Port::Scale(float x, float y)
{
	FlushMyText();
	sfont= 0;
	DevScale(x*scalex, y*scaley);
}

void Port::Translate(const Point &oo)
{
	Point o(oo);
	ScalePoint(this, o);
	origin+= o;
}

bool Port::Visible(Ink *ink, const Rectangle &r)
{
	if (ink != gInkNone && (WindowSystem::fullscreen || cliprect.Intersects(r))) {
		if (scale & 2) {
			DevClip(cliprect);
			scale&= ~2;
		}
		FlushMyText();
		InstallInk(ink);
		return TRUE;
	}
	return FALSE;
}

//---- graphic primitives ------------------------------------------------------

void Port::StrokeLine(Ink *ink, int psz, GrLineCap cap, const Point &pp1,
								const Point &pp2)
{
	if (ignore)
		return;
	Point p1(pp1), p2(pp2);
	if (scale & 1) {
		ScalePoint(this, p1);
		ScalePoint(this, p2);
		ScaleInt(this, psz);
	}
	p1+= origin;
	p2+= origin;
	Rectangle rr(NormRect(p1,p2).Expand(Math::Max(2, psz)/2));
	if (Visible(ink, rr)) {
		p1+= fso;
		p2+= fso;
		DevStrokeLine(psz, rr, cap, p1, p2);
	}
}

void Port::StrokeRect(Ink *ink, int psz, const Rectangle &rr)
{
	if (ignore)
		return;
	Rectangle r(rr);
	if (scale & 1) {
		ScaleRect(this, r);
		ScaleInt(this, psz);
	}
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevStrokeRect(psz, r);
	}
}

void Port::FillRect(Ink *ink, const Rectangle &rr)
{
	if (ignore)
		return;
	Rectangle r(rr);
	ScaleRect(this, r);
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		if (r.extent.x > 16000)
			r.extent.x= 16000;
		if (r.extent.y > 16000)
			r.extent.y= 16000;
		DevFillRect(r);
	}
}

void Port::StrokeOval(Ink *ink, int psz, const Rectangle &rr)
{
	if (ignore)
		return;
	Rectangle r(rr);
	if (scale & 1) {
		ScaleRect(this, r);
		ScaleInt(this, psz);
	}
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevStrokeOval(psz, r);
	}
}

void Port::FillOval(Ink *ink, const Rectangle &rr)
{
	if (ignore)
		return;
	Rectangle r(rr);
	ScaleRect(this, r);
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevFillOval(r);
	}
}

void Port::StrokeRRect(Ink *ink, int psz, const Rectangle &rr, const Point &d)
{
	if (ignore)
		return;
	Point dia(d);
	Rectangle r(rr);
	if (scale & 1) {
		ScaleRect(this, r);
		ScalePoint(this, dia);
		ScaleInt(this, psz);
	}
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevStrokeRRect(psz, r, dia);
	}
}

void Port::FillRRect(Ink *ink, const Rectangle &rr, const Point &d)
{
	if (ignore)
		return;
	Point dia(d);
	Rectangle r(rr);
	if (scale & 1) {
		ScaleRect(this, r);
		ScalePoint(this, dia);
	}
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevFillRRect(r, dia);
	}
}

void Port::StrokeWedge(Ink *ink, int psz, GrLineCap cap, const Rectangle &rr, int s, int l)
{
	if (ignore)
		return;
	if (l >= 360) {
		StrokeOval(ink, psz, rr);
		return;
	}
	while (s < 0)
		s+= 360;
	while (s >= 360)
		s-= 360;
	Rectangle r(rr);
	if (scale & 1) {
		ScaleRect(this, r);
		ScaleInt(this, psz);
	}
	r.origin+= origin;
	if (Visible(ink, r.WedgeBBox(s, l))) {
		r.origin+= fso;
		DevStrokeWedge(psz, cap, r, s, l);
	}
}

void Port::FillWedge(Ink *ink, const Rectangle &rr, int s, int l)
{
	if (ignore)
		return;
	if (l >= 360) {
		FillOval(ink, rr);
		return;
	}
	while (s < 0)
		s+= 360;
	while (s >= 360)
		s-= 360;
	Rectangle r(rr);
	ScaleRect(this, r);
	r.origin+= origin;
	if (Visible(ink, r.WedgeBBox(s, l))) {
		r.origin+= fso;
		DevFillWedge(r, s, l);
	}
}

void Port::StrokePolygon(const Point &a, Ink *ink, Point *pts,
			int npts, GrPolyType t, int psz, GrLineCap cap)
{
	if (ignore || pts == 0 || npts <= 0)
		return;
	Point at(a);
	Point *p= (Point*) Alloca(npts * sizeof(Point));
	Rectangle r(BoundingBox(npts, pts, p));
	r.origin+= at;
	if (scale & 1) {
		ScaleRect(this, r);
		ScaleInt(this, psz);
		for (int i= 0; i < npts; i++)
			ScalePoint(this, p[i]);
	}
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevStrokePolygon(r, p, npts, t, psz, cap);
	}
	Freea(p);
}

void Port::FillPolygon(const Point &a, Ink *ink, Point *pts, int npts, GrPolyType t)
{
	if (ignore || pts == 0 || npts <= 0)
		return;
	Point at(a);
	Point *p= (Point*) Alloca(npts * sizeof(Point));
	Rectangle r(BoundingBox(npts, pts, p));
	r.origin+= at;
	if (scale & 1) {
		ScaleRect(this, r);
		for (int i= 0; i < npts; i++)
			ScalePoint(this, p[i]);
	}
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevFillPolygon(r, p, npts, t);
	}
	Freea(p);
}

void Port::ShowBitmap(Ink *ink, const Rectangle &rr, Bitmap *bm)
{
	if (ignore)
		return;
	Rectangle r(rr);
	ScaleRect(this, r);
	r.origin+= origin;
	if (Visible(ink, r)) {
		r.origin+= fso;
		DevShowBitmap(r, bm);
	}
}

void Port::ShowPicture(const Rectangle &rr, Picture *pic)
{
	if (ignore || pic == 0)
		return;
	Rectangle r(rr);
	ScaleRect(this, r);
	r.origin+= origin;
	//if (Visible(gInkBlack, r)) {
		r.origin+= fso;
		DevShowPicture(r, pic);
	//}
}

void Port::GiveHint(int code, int len, void *vp)
{
	if (vp && len < 0)
		len= strlen((const char*) vp) + 1;

	switch (code) {

	case eHintBatch:
		{
		Rectangle r(*((Rectangle*)vp));
		ScaleRect(this, r);
		r.origin+= origin+fso;
		DevGiveHint(code, len, &r);
		}
		break;
		
	default:
		DevGiveHint(code, len, vp);
		break;
	}
}

//---- Text --------------------------------------------------------------------

void Port::SetFamily(GrFont fid)
{
	textfont= new_Font(fid, textfont->Size(), textfont->Face());
	sfont= 0;
}

void Port::SetSize(int ps)
{
	textfont= new_Font(textfont->Fid(), ps, textfont->Face());
	sfont= 0;
}

void Port::SetFace(GrFace face)
{
	textfont= new_Font(textfont->Fid(), textfont->Size(), face);
	sfont= 0;
}

void Port::SetFont(Font *fd)
{
	if (textfont != fd) {
		textfont= fd;
		sfont= 0;
	}
}
//---- text batch --------------------------------------------------------------

void Port::flushtext()
{
	if (scale & 2) {
		DevClip(cliprect);
		scale&= ~2;
	}
	InstallInk(tbink);
	//Rectangle r(tbbbox);
	//r.origin+= fso;
	//DevShowTextBatch(&r, tbpos+origin+fso);
	DevShowTextBatch(tbbbox, tbpos+origin+fso);
	tbport= 0;
}

int Port::ShowChar(Font *fdp, Ink *ink, const Point &pos, Rune c)
{
	byte s[2];
	s[0]= c;
	s[1]= 0;
	return ShowString(fdp, ink, pos, s, 1);
}

Ink *gTbInk;

int Port::ShowString(Font *fdp, Ink *ink, const Point &pp, byte *s, int l)
{
	Point pos(pp);
	Rectangle rr;
	register int w, sl= 0;
	byte c, *ss= s;
	int ll= l;

	if (ignore || s == 0)
		return 0;

	if (fdp != lastfdp) {
		lastfdp= fdp;
		sfont= 0;
	}

	if (scale & 1) {
		if (sfont == 0) {
			int ss= fdp->Size();
			ScaleInt(this, ss);
			sfont= new_Font(fdp->Fid(), ss, fdp->Face());
		}
		fdp= sfont;
		ScalePoint(this, pos);
	}
		
	rr.origin= pos;
	rr.origin+= origin;
	rr.origin.y-= fdp->Ascender();
	rr.extent.y= fdp->Spacing();

	while (l-- && (c= *s++)) {
		rr.extent.x= w= fdp->Width(c);
		if (WindowSystem::fullscreen || cliprect.Intersects(rr)) {
			if (scale & 2) {
				DevClip(cliprect);
				scale&= ~2;
			}
			if (this != tbport || ink != tbink)
				FlushAnyText();
			gTbInk= ink;
			DevShowChar(fdp, pos-tblastpos, c, rr);
			if (tbport == 0) {
				tbport= this;
				tbink= ink;
				tbpos= pos;
				tbbbox= rr;
			} else
				tbbbox.Merge(rr);
			tblastpos.x= pos.x + w;
			tblastpos.y= pos.y;
		}
		rr.origin.x+= w;
		pos.x+= w;
		sl+= w;
	}
	if (scale & 1)
		return lastfdp->Width(ss, ll);
	return sl;
}

//---- device dependent methods ------------------------------------------------

void Port::DevClip(const Rectangle&)
{
	AbstractMethod("DevClip");
}

void Port::DevFillRect(const Rectangle&)
{
	AbstractMethod("DevFillRect");
}

void Port::DevShowBitmap(const Rectangle&, Bitmap*)
{
	AbstractMethod("DevShowBitmap");
}

void Port::DevShowChar(Font*, const Point&, Rune, const Rectangle&)
{
}

void Port::DevShowTextBatch(const Rectangle&, const Point&)
{
	AbstractMethod("DevShowTextBatch");
}

void Port::DevGiveHint(int, int, void*)
{
}

void Port::DevSetPattern(Bitmap*)
{
}

void Port::DevSetColor(RGBColor*)
{
}

void Port::DevSetOther(int)
{
}

bool Port::DevImageCacheBegin(ImageCache*, const Rectangle&)
{
	return TRUE;    // need redraw
}

void Port::DevImageCacheEnd(ImageCache*)
{
}

bool Port::DevScale(float ssx, float ssy)
{
	scalex= ssx;
	scaley= ssy;
	if (scalex != 1.0 || scaley != 1.0)
		scale|= 1;
	else
		scale&= ~1;
	return FALSE;   // FALSE means: can't scale
}

void Port::DevShowPicture(const Rectangle &r, Picture *pic)
{
	pic->Show(r, this);
}

//---- window system independent graphic methods -------------------------------

Point Port::DevDrawArrow(int psz, const Point &s, const Point &e)
{
	int arrowwidth= Math::Max(6, psz*4+2), arrowlen= Math::Max(9, psz*7);
	Point c, pts[3], ee(arrowwidth/2, arrowlen);

	if (Length(e-s) < arrowlen)
		return e;     // too short, give up

	double ph= Phi(e-s), rot= Phi(ee), hyp= Length(ee);

	pts[0]= e;
#ifdef PETERFIX
// special case: going straight down
	pts[1]= e + Point(-(arrowwidth/2),-arrowlen);
	pts[2]= e + Point(arrowwidth/2,-arrowlen);
#else
	pts[1]= e + PolarToPoint(ph+rot, hyp, hyp);
	pts[2]= e + PolarToPoint(ph-rot, hyp, hyp);
#endif
	c= Half(pts[1]+pts[2]);

	Rectangle rr(BoundingBox(3, pts));
	DevFillPolygon2(rr, pts, 3, ePolyDefault);

	return c;
}

void Port::DevStrokeLine(int psz, const Rectangle &r, GrLineCap cap,
				const Point &start, const Point &end)
{
	if (cap) {
		Point s(start), e(end);
		if (cap & eStartArrow)
			s= DevDrawArrow(psz, end, start);
		if (cap & eEndArrow)
			e= DevDrawArrow(psz, start, end);
		DevStrokeLine2(psz, r, cap, s, e);
	} else
		DevStrokeLine2(psz, r, cap, start, end);
}

void Port::DevStrokeRect(int psz, const Rectangle &r)
{
	int rpsz= (psz <= 0) ? 1 : psz;
		
	rpsz*= 2;
	if (rpsz >= r.extent.x || rpsz >= r.extent.y)
		DevFillRect(r);
	else
		DevStrokeRect2(psz, r);
}

//---- splines -----------------------------------------------------------------

/*
* bez: Subdivide a Bezier spline, until it is thin enough to be
*      considered a line. Store line point in static array points.
*/

#define FIX(a) (((int) (a)) << 16)
#define INT(a) (((a) + (1 << 15)) >> 16 )
#define DELTA 1

static void bez(Point **gPp, int x0, int y0, int x1, int y1, int x2, int y2, int x3, int y3)
{
	int maxx= Math::Max(x0, x3), minx= Math::Min(x0, x3),
		maxy= Math::Max(y0, y3), miny= Math::Min(y0, y3);
	register int tx, ty;

	if (x1 >= minx && x1 <= maxx && y1 >= miny && y1 <= maxy
				&& x2 >= minx && x2 <= maxx && y2 >= miny && y2 <= maxy) {
		register int ax, ay, dx= INT(x3-x0), dy= INT(y3-y0);
		if (dx == 0 || dy == 0) {
			(*gPp)->x= INT(x3);
			(*gPp)++->y= INT(y3);
			return;
		}
		ax= ((dy*INT(x1-x0))/dx)+INT(y0-y1);
		ay= ((dx*INT(y1-y0))/dy)+INT(x0-x1);
		if (Math::Abs(ax*ay) <= DELTA) {
			(*gPp)->x= INT(x3);
			(*gPp)++->y= INT(y3);
			return;
		}
		ax= ((dy*INT(x2-x0))/dx)+INT(y0-y2);
		ay= ((dx*INT(y2-y0))/dy)+INT(x0-x2);
		if (Math::Abs(ax*ay) <= DELTA) {
			(*gPp)->x= INT(x3);
			(*gPp)++->y= INT(y3);
			return;
		}
	}
		
	tx= (x0 >> 3) + 3 * (x1 >> 3) + 3 * (x2 >> 3) + (x3 >> 3);
	ty= (y0 >> 3) + 3 * (y1 >> 3) + 3 * (y2 >> 3) + (y3 >> 3);
	bez(gPp, x0, y0, (x0 >> 1) + (x1 >> 1), (y0 >> 1) + (y1 >> 1),
				(x0 >> 2) + (x1 >> 1) + (x2 >> 2),
				(y0 >> 2) + (y1 >> 1) + (y2 >> 2),
				tx, ty);
	bez(gPp, tx, ty, (x3 >> 2) + (x2 >> 1) + (x1 >> 2),
				(y3 >> 2) + (y2 >> 1) + (y1 >> 2),
				(x3 >> 1) + (x2 >> 1), (y3 >> 1) + (y2 >> 1),
				x3, y3);
}

static int makespline(Point *pts, Point *p, int n, GrPolyType)
{
	register int i;
	Point *gPp= pts;

	gPp->x= p[0].x;
	(gPp++)->y= p[0].y;
	for (i= 0; i < n-1; i+= 3)
		bez(&gPp, FIX(p[i+0].x), FIX(p[i+0].y), FIX(p[i+1].x), FIX(p[i+1].y),
				FIX(p[i+2].x), FIX(p[i+2].y), FIX(p[i+3].x), FIX(p[i+3].y));
		
	return gPp - pts;
}

void Port::DevStrokePolygon(const Rectangle &r,
				Point *pts, int npts, GrPolyType t, int psz, GrLineCap cap)
{
	Point *tmppts= 0;

	if (cap & eStartArrow)
		DevDrawArrow(psz, pts[1]+r.origin, pts[0]+r.origin);
	if (cap & eEndArrow)
		DevDrawArrow(psz, pts[npts-2]+r.origin, pts[npts-1]+r.origin);
	if (t & ePolyBezier) {
		tmppts= (Point*) Alloca(sizeof(Point) * npts * 30);
		int nn= makespline(tmppts, pts, npts, t);
		if (nn > 0) {
			pts= tmppts;
			npts= nn;
		}
	}
	DevStrokePolygon2(r, pts, npts, ePolyDefault, psz, cap);
	Freea(tmppts);
}

void Port::DevFillPolygon(const Rectangle &r, Point *pts, int npts, GrPolyType t)
{
	Point *tmppts= 0;
	if (t & ePolyBezier) {
		tmppts= (Point*) Alloca(sizeof(Point) * npts * 30);
		int nn= makespline(tmppts, pts, npts, t);
		if (nn > 0) {
			pts= tmppts;
			npts= nn;
		}
	}
	DevFillPolygon2(r, pts, npts, ePolyDefault);
	Freea(tmppts);
}

void Port::DevStrokeWedge(int psz, GrLineCap cap, const Rectangle &r, int s, int d)
{
	int rpsz= (psz <= 0) ? 1 : psz;
	rpsz*= 2;
	if (rpsz >= r.extent.x || rpsz >= r.extent.y) {
		DevFillWedge(r, s, d);
		return;
	}
	if (r.extent.x < 4 || r.extent.y < 4) {
		DevStrokeRect(psz, r);
		return;
	}
	if (cap & eStartArrow) {
		DevDrawArrow(psz, r.OvalAngleToPoint(s+10), r.OvalAngleToPoint(s));
		// s+= 10; d-= 10;
	}
	if (cap & eEndArrow) {
		DevDrawArrow(psz, r.OvalAngleToPoint(s+d-10), r.OvalAngleToPoint(s+d));
		// d-= 10;
	}
	DevStrokeWedge2(psz, cap, r, s, d);
}

void Port::DevFillWedge(const Rectangle &r, int s, int d)
{
	if (r.extent.x < 4 || r.extent.y < 4)
		DevFillRect(r);
	else
		DevFillWedge2(r, s, d);
}

static void Wedge2Polygon(Point *pts, int &n, const Point &e2, float start, float len)
{
	if (len < 10)    // consider it a line
		pts[n++]= PolarToPoint(start, e2.x, e2.y) + e2;
	else {                  // subdivide
		Wedge2Polygon(pts, n, e2, start, len/2);
		Wedge2Polygon(pts, n, e2, start+len/2, len/2);
	}
}

void Port::DevStrokeWedge2(int psz, GrLineCap cap, const Rectangle &rr,
								int start, int len)
{
	Rectangle r(rr);
	Point *pts= (Point*) Alloca(sizeof(Point) * len);
	Point e2((r.extent-psz)/2);
	int n= 0;

	Wedge2Polygon(pts, n, e2, start, len);
	pts[n++]= PolarToPoint(start+len, e2.x, e2.y) + e2;
	r.origin+= psz/2;
	DevStrokePolygon2(r, pts, n, ePolyDefault, psz, cap);
	Freea(pts);
}

void Port::DevFillWedge2(const Rectangle &r, int start, int len)
{
	Point *pts= (Point*) Alloca(sizeof(Point) * len);
	Point e2(r.extent/2);
	int n= 0;

	pts[n++]= e2;
	Wedge2Polygon(pts, n, e2, start, len);
	pts[n++]= PolarToPoint(start+len, e2.x, e2.y) + e2;
	DevFillPolygon2(r, pts, n, ePolyDefault);
	Freea(pts);
}

void Port::DevStrokeRRect(int psz, const Rectangle &r, const Point &d)
{
	Point dia(d);
	int rpsz= (psz <= 0) ? 1 : psz;
		
	rpsz*= 2;
	if (rpsz >= r.extent.x || rpsz >= r.extent.y) {
		DevFillRRect(r, dia);
		return;
	}
	if (Math::Odd(dia.x))    // force to be even
		dia.x--;
	if (Math::Odd(dia.y))    // force to be even
		dia.y--;
		
	if (dia.x >= r.extent.x && dia.y >= r.extent.y)
		DevStrokeOval(psz, r);
	else
		DevStrokeRRect2(psz, r, Min(dia, r.extent));
}

void Port::DevFillRRect(const Rectangle &r, const Point &d)
{
	Point dia(d);

	if (Math::Odd(dia.x))    // force to be even
		dia.x--;
	if (Math::Odd(dia.y))    // force to be even
		dia.y--;
		
	if (dia.x > r.extent.x && dia.y > r.extent.y)
		DevFillOval(r);
	else
		DevFillRRect2(r, Min(dia, r.extent));
}

void Port::DevStrokeOval(int psz, const Rectangle &r)
{
	int rpsz= (psz <= 0) ? 1 : psz;

	rpsz*= 2;
	if (rpsz >= r.extent.x || rpsz >= r.extent.y)
		DevFillOval(r);
	else if (r.extent.x < 4 || r.extent.y < 4)
		DevStrokeRect(psz, r);
	else
		DevStrokeOval2(psz, r);
}

void Port::DevFillOval(const Rectangle &r)
{
	if (r.extent.x < 4 || r.extent.y < 4)
		DevFillRect(r);
	else
		DevFillOval2(r);
}

void Port::DevStrokeOval2(int psz, const Rectangle &r)
{
	DevStrokeRRect2(psz, r, r.extent);
}

void Port::DevFillOval2(const Rectangle &r)
{
	DevFillRRect2(r, r.extent);
}

//---- abstract methods --------------------------------------------------------

void Port::DevStrokeRect2(int, const Rectangle&)
{
	AbstractMethod("DevStrokeRect2");
}

void Port::DevStrokeRRect2(int, const Rectangle&, const Point&)
{
	AbstractMethod("DevStrokeRRect2");
}

void Port::DevFillRRect2(const Rectangle&, const Point&)
{
	AbstractMethod("DevFillRRect2");
}

void Port::DevStrokePolygon2(const Rectangle&, Point*, int, GrPolyType, int, GrLineCap)
{
	AbstractMethod("DevStrokePolygon2");
}

void Port::DevFillPolygon2(const Rectangle&, Point*, int, GrPolyType)
{
	AbstractMethod("DevFillPolygon2");
}

void Port::DevStrokeLine2(int, const Rectangle&, GrLineCap, const Point&, const Point&)
{
	AbstractMethod("DevStrokeLine2");
}

//---- Utilities ---------------------------------------------------------------

void DrawShadow(const Rectangle &r, const Point &delta, Ink* fg, Ink *bg)
{
	if (delta != gPoint0) {
		Rectangle sr(r);
		sr.origin.x+= sr.extent.x;
		sr.origin.y+= delta.y;
		sr.extent.x= delta.x;
		GrPaintRect(sr, bg);
		sr= r;
		sr.origin.x+= delta.x;
		sr.origin.y+= sr.extent.y;
		sr.extent.y= delta.y;
		GrPaintRect(sr, bg);
	}
	GrPaintRect(r, fg);
	GrStrokeRect(r);
}

